package edu.hawaii.halealohacli.processor;

import java.io.IOException;
import java.util.Arrays;
import org.wattdepot.client.WattDepotClient;
import edu.hawaii.halealohacli.command.CommandManager;

/**
 * Command Processor.
 * 
 * @author Group BackR
 * 
 */
public class CommandProcessor {

  /**
   * Processes the user input string, runs the command and returns the output.
   * 
   * @param input The user input.
   * @param client The WattDepotClient server.
   * @return The data retrieved from the command class.
   */
  @SuppressWarnings("rawtypes")
  public String process(String input, WattDepotClient client) {
    // Initialize output.
    String output = "";
    String [] exceptions = {"monitor-power", "set-baseline"};
    // Check if output is quit. Return null.
    if (input.equalsIgnoreCase("quit")) {
      return null;
    }
    else {
      String[] userInput = null;
      // Checks to see if a user only entered a space character as a command.
      if (input.startsWith(" ")) {
        // Manually create an array of two space characters.
        String[] tempInput = { " ", " " };
        userInput = tempInput;
      }
      else {
        // Split the user input into an array by the space character.
        userInput = input.split(" ");
      }
      if (userInput[0].equalsIgnoreCase("quit")) {
        output = "The quit command takes in no arguments. Try again.";
      }
      else {
        // Find he matching command class.
        Class commandClass = getCommandClass(userInput[0]);
        // If user provided invalid class.
        if (commandClass == null) {
          output = "\"" + userInput[0] + "\" is not a valid command! Please try again.";
        }
        else {
          // Parameters for all methods except run() is blank.
          Class[] params = {};
          // Parameters for run() method.
          Class[] runParams = { String[].class, WattDepotClient.class };
          // Get the number of arguments the specified command class takes.
          int numParams =
              Integer.parseInt(runMethod(commandClass, "getNumParams", params, null, null));
          // Check if user provided the correct amount of arguments
          if (numParams == userInput.length - 1) {
            // Correct number of parameters. Run method and get output.
            output = runMethod(commandClass, "run", runParams, userInput, client);
          }
          // Added in exceptions for methods with optional arguments.
          else if ((Arrays.asList(exceptions).contains(userInput[0]))) {
            output = runMethod(commandClass, "run", runParams, userInput, client);
          }
          else {
            // Invalid number of parameters.
            output =
                "Invalid number of arguments\n" + (userInput.length - 1) + " entered. " + numParams
                    + " expected. Try again.";
          }
        }
      }
    }
    // Return data to main program.
    return output;
  }

  /**
   * Finds the matching command class from the user input.
   * 
   * @param command The first arguments of the user input.
   * @return The matching command class.
   */
  @SuppressWarnings({ "rawtypes" })
  public Class getCommandClass(String command) {
    // Initializes thisClass to use as the class to instantiate if found.
    Class commandClass = null;
    try {
      // Construct new CommandManager.
      CommandManager manager = new CommandManager();
      // Get a list of all classes in the Command package.
      Class[] commandList = manager.getClasses();
      // Initialize a class array of parameters to search for the getCommand method.
      Class[] params = {};
      // Iterate through the commandList.
      for (Class tempClass : commandList) {
        // If input matches the command, set thisClass as the tempClass currently accessed.
        if (runMethod(tempClass, "getCommand", params, null, null).equals(command)) {
          commandClass = tempClass;
        }
      }
    }
    catch (ClassNotFoundException e) {
      System.out.println("Could not find " + command + " command as a class.");
    }
    catch (IOException e) {
      System.out.println("An unexpected file error has occured. Please try again.");
    }
    catch (SecurityException e) {
      System.out.println("Could not access command.");
    }
    catch (IllegalArgumentException e) {
      System.out.println("Invalid nuber of arguments given! Please try again.");
    }
    return commandClass;
  }

  /**
   * Runs the method of a given command class.
   * 
   * @param commandClass The command class to run.
   * @param methodName The method to access.
   * @param runParams The parameters to pass to the method.
   * @param input The user input.
   * @param client The WattDepotClient server.
   * @return The data retrieved from the command class.
   */
  @SuppressWarnings("rawtypes")
  public String runMethod(Class commandClass, String methodName, Class[] runParams, String[] input,
      WattDepotClient client) {
    // Create new instance of CommandManager.
    CommandManager manager = new CommandManager();
    // Run the given class and method, then return the data sent back by the command class.
    return manager.run(commandClass, methodName, runParams, input, client);
  }
}
